/**
 * @file memAndSem.c
 * @author Mario Alvarez (mario.alvarezp@estudiante.uam.es) J. Angel Gonzalez (joseangel.gonzalezp@estudiante.uam.es)
 * @date 26-03-2011
 *
 * Biblioteca de gestion de acceso a recursos compartidos mediante semaforos. Incluye funciones de control de acceso
 * a zona critica.
*/

#include "memAndSem.h"

typedef void (*sigHandler) (int);

short endSleep=0;													/*!< Flag de finalizacion de espera*/
pthread_mutex_t endSleep_mutex = PTHREAD_MUTEX_INITIALIZER;			/*!< Semaforo de proteccion de variable endSleep*/

/**
* @brief Funcion manejadora de sennal de wakeup para finalizar la espera.
*
* @param sennal Sennal interruptora de espera.
*/
void sleepNotify(int sennal);

int getMutex(int key)
{
	int semId;
	
	semId = semget(key, 1, IPC_EXCL|IPC_CREAT|SHM_R|SHM_W);			/*Reserva de semaforos en exclusiva*/
	if(semId==-1){
	  	semId= semget(key, 1, IPC_CREAT|SHM_R|SHM_W);				/*Reserva de semaforo sin exclusividad*/
	  	
	  	return semId;
  	}
  	
  	if(semctl(semId, 0, SETVAL, 1)==-1){								/*Inicializacion del semaforo*/
  		return -1;
  	}
  	
  	return semId;
}


int freeMutex(int id)
{
	return 0;
}

int down(int id, int numsem)
{
	struct sembuf op;
	int ret=-1;
	
	op.sem_num=numsem;						/*Posicion del semaforo en el array*/
	op.sem_op=-1;							/*Decrementar*/
	op.sem_flg=0;							/*Sin flags*/
	
	do{
		ret=semop(id, &op, 1);
	}while(ret==-1 && errno==EINTR);		/*Reiterar la espera en caso de interrupcion por sennal (errno==EINTR)*/
	
	
	return ret;
}

int up(int id, int numsem)
{
	struct sembuf op;
	int ret=-1;
	
	op.sem_num=numsem;						/*Posicion del semaforo en el array*/
	op.sem_op=1;							/*Incrementar*/
	op.sem_flg=0;							/*Sin flags*/
	
	ret=semop(id, &op, 1);
	
	return ret;
}

int downDireccionable(int id, int numsem, short *bifurcar, void (*alternativeFunction) (void))
{
	struct sembuf op;
	int ret=-1;

	op.sem_num=numsem;						/*Posicion del semaforo en el array*/
	op.sem_op=-1;							/*Decrementar*/
	op.sem_flg=0;							/*Sin flags*/
	
	do{
		ret=semop(id, &op, 1);
	}while(ret==-1 && !(*bifurcar) && errno!=EIDRM);		/*Reiterar mientras la espera sea interrumpida de forma anomala y el flag de bifurcacion se encuentre a 0*/
	
	if(*bifurcar){							/*Flag de bifurcacion activo*/
		alternativeFunction();				/*Llamada a funcion alternativa*/
		ret=1;								/*Salida correcta (Actuo segun lo previsto)*/
	}

	return ret;
}

void sleepNotify(int sennal)
{
	puts("Entrando en sleepNotify\n");
	if(sennal==SIGWAKEUP){							/*Recepcion de sennal de wakeup*/
		pthread_mutex_lock(&endSleep_mutex);{
			puts("EndSleep=1");
			endSleep=1;								/*Fin de la espera*/
		}pthread_mutex_unlock(&endSleep_mutex);
	}
	
	return;
}

int sleepUntilWakeup(long timeout)
{
	struct timespec rqtp;
	struct timespec rmtp;
	long sec, nsec;
	int status;
	sigHandler oldHandler;
	
	sec=timeout/((long)(10e9));
	nsec=timeout % ((long)(10e9));
	
	rqtp.tv_sec=sec;
	rqtp.tv_nsec=nsec;
	rmtp.tv_sec=sec;
	rmtp.tv_nsec=nsec;
	
	/*Captura del manejador anterior de SIGWAKEUP*/
	oldHandler=signal(SIGWAKEUP, sleepNotify);
	if(oldHandler==SIG_ERR){
		return MEM_AND_SEM_ERR;
	}
	
	/*Inicio de la espera*/
	pthread_mutex_lock(&endSleep_mutex);
  	{	
		endSleep=0;
	}
  	pthread_mutex_unlock(&endSleep_mutex);	
  
  	/*Espera hasta recepcion de sennal de wakeup*/
	do{
		puts("Entrando en nanosleep");
		status=nanosleep(&rqtp, &rmtp);
		puts("Fuera de nanosleep");
	}while(status!=0 && endSleep!=1);
	//while(status==-1 && !(errno==EINTR && endSleep==1));
    printf("SleepUntilWakeup: Hilo=%d\n", pthread_self());
	printf("status=%d\n", status);
	printf("endSleep=%d\n", endSleep);
	printf("ERRNO=%d - EINTR=%d\n", errno, EINTR);
	fflush(NULL);
	
	/*Reestauracion del manejador de SIGWAKEUP*/
	//if(signal(SIGWAKEUP, oldHandler)==SIG_ERR){
	//	return MEM_AND_SEM_ERR;
	//}
	
	if(status==0){
		return MEM_AND_SEM_TIMEOUT;
	}
	
	return MEM_AND_SEM_OK;
}

int wakeup(pid_t pid)
{
	/*Envio de la sennal SIGWAKEUP al proceso de PID pid*/
	if(kill(pid, SIGWAKEUP)!=0){
		return MEM_AND_SEM_ERR;
	}
	
	return MEM_AND_SEM_OK;
}

